#pragma once
#include<iostream>
#include<set>
#include<string>
#include<functional>
#include<string.h>

#include"Log.hpp"
#include"InetAddr.hpp"
#include"PthreadPool.hpp"

class Compare
{
public:
    bool operator()(InetAddr addr1,InetAddr addr2)
    {
        std::string s1=addr1.Ip();
        std::string s2=addr2.Ip();
        s1+=std::to_string(addr1.Port());
        s2+=std::to_string(addr2.Port());
        return s1>s2;
    }
};

class Chat
{
private:
    using func=std::function<void(void)>;
public:
    Chat()
    :_tp(ThreadPool<func>(7))
    {
        _tp.Start();
    }

    void AddUser(InetAddr sender)
    {
        if(_user.end()!=_user.find(sender))
        {
            return;
        }
        auto ret=_user.insert(sender);
        if(ret.second)
        {
            Log(DEBUG,"add user [%s %d] successfully 在线用户 %ld\n",sender.Ip().c_str(),sender.Port(),_user.size());
            return;
        }
        Log(DEBUG,"add user [%s %d] fail 在线用户 %ld\n",sender.Ip().c_str(),sender.Port(),_user.size());
    }

    void DeleteUser(InetAddr sender)
    {
        if(_user.erase(sender)>0)
        {
            Log(DEBUG,"delete user [%s] successfully\n",sender.Ip().c_str());
            return;
        }
        Log(ERROR,"delete user [%s] fail\n",sender.Ip().c_str());
    }

    void Send(int sockfd,InetAddr sender,std::string message)
    {
        char broadcast_message[1024];
        std::string tmp_message;
        tmp_message+="Ip[";
        tmp_message+=sender.Ip();
        tmp_message+="] Port[";
        tmp_message+=std::to_string(sender.Port());
        tmp_message+="]: ";
        tmp_message+=message;
        strcpy(broadcast_message,tmp_message.c_str());
        for(auto e:_user)
        {
            sockaddr_in client=e.GetAddr();
            int n=sendto(sockfd,broadcast_message,sizeof(broadcast_message),0,(struct sockaddr*)(&client),sizeof(client));
            if(n<=0)
            {
                Log(ERROR,"send message [%s] to ip [%s] error\n",broadcast_message,e.Ip().c_str());
                continue;
            }
            Log(DEBUG,"send message [%s] to ip [%s] successfully\n",broadcast_message,e.Ip().c_str());
        }
    }

    void Broadcast(int sockfd,InetAddr sender,std::string message)
    {
        AddUser(sender);
        _tp.PushTask(std::bind(&Chat::Send,this,sockfd,sender,message));
        if("quit"==message)
        {
            DeleteUser(sender);
        }
    }

    ~Chat()
    {
        _tp.Wait();
    }

private:
    ThreadPool<func> _tp;
    set<InetAddr,Compare> _user;
};